图像和内存
我们在Nibabel图像中看到,从磁盘加载的图像通常是代理图像。代理映像是具有dataobj
不是numpy数组属性的映像,而是可以从磁盘读取数组数据的数组代理。
>>> import os
>>> import numpy as np
>>> from nibabel.testing import data_path
>>> example_file = os.path.join(data_path, 'example4d.nii.gz')
>>> import nibabel as nib
>>> img = nib.load(example_file)
>>> img.dataobj
<nibabel.arrayproxy.ArrayProxy object at ...>
当load
图像时,Nibabel不会从代理加载图像数组。它等待,直到你问阵列数据。要求数组数据的标准方法是调用该get_data()
方法:
>>> data = img.get_data()
>>> data.shape
(128, 96, 24, 2)
我们还在代理和缓存中看到,这个调用get_data()
将(默认情况下)将数组数据加载到内部图像缓存中。该图像将下一次调用的缓存副本返回到get_data()
:
>>> data_again = img.get_data()
>>> data is data_again
True
如果您想快速重复访问图像数组数据,则此行为非常方便。不利的一面是图像保留了对图像数据数组的引用,因此在删除图像对象之前,数组不能从内存中清除。您可能更喜欢从磁盘继续加载数组,而不是将缓存的副本保留在图像中。
本页介绍使用图像阵列代理来节省内存和时间的方法。
使用in_memory
来检查高速缓存的状态
您可以使用该in_memory
属性来检查图像是否缓存了数组。
该in_memory
属性对于数组图像始终为True,因为图像数据始终是内存中的一个数组:
>>> array_data = np.arange(24, dtype=np.int16).reshape((2, 3, 4))
>>> affine = np.diag([1, 2, 3, 1])
>>> array_img = nib.Nifti1Image(array_data, affine)
>>> array_img.in_memory
True
对于代理映像,in_memory
当数组不在缓存中时属性为False,在缓存中为True时:
>>> img = nib.load(example_file)
>>> img.in_memory
False
>>> data = img.get_data()
>>> img.in_memory
True
使用uncache
大家知道,代理映像有get_data()
缓存中的数组,返回缓存数组:
>>> data_again = img.get_data()
>>> data_again is data # same array returned from cache
True
您可以使用以下uncache()
方法解包代理映像:
>>> img.uncache()
>>> img.in_memory
False
>>> data_once_more = img.get_data()
>>> data_once_more is data # a new copy read from disk
False
uncache()
如果图像是数组图像,或者缓存已经是空的,则不起作用。
修改get_data()
代理映像返回的数组时,需要小心,因为这样uncache
会更改从get_data()
以下位置返回的结果:
>>> proxy_img = nib.load(example_file)
>>> data = proxy_img.get_data() # array cached and returned
>>> data[0, 0, 0, 0]
0
>>> data[0, 0, 0, 0] = 99 # modify returned array
>>> data_again = proxy_img.get_data() # return cached array
>>> data_again[0, 0, 0, 0] # cached array modified
99
到目前为止,代理图像的行为与数组图像相同。uncache()
对数组图像没有任何影响,但它对代理图像的返回数组有影响:
>>> proxy_img.uncache() # cached array discarded from proxy image
>>> data_once_more = proxy_img.get_data() # new copy of array loaded
>>> data_once_more[0, 0, 0, 0] # array modifications discarded
0
节省内存
取消数组
如果您不希望图像将数组保存在其内部缓存中,则可以使用以下uncache()
方法:
>>> img.uncache()
使用数组代理而不是get_data()
dataobj
代理映像的属性是一个数组代理。我们可以请求代理直接返回数组传递dataobj
给numpyasarray
函数:
>>> proxy_img = nib.load(example_file)
>>> data_array = np.asarray(proxy_img.dataobj)
>>> type(data_array)
<... 'numpy.ndarray'>
这也适用于阵列图像,因为np.asarray
返回数组:
>>> array_img = nib.Nifti1Image(array_data, affine)
>>> data_array = np.asarray(array_img.dataobj)
>>> type(data_array)
<... 'numpy.ndarray'>
如果你想避免缓存,你可以避免get_data()
和总是使用np.asarray(img.dataobj)
。
使用caching
关键字get_data()
该get_data()
函数的默认行为是始终填充缓存,如果它是空的。这对应于关键字的默认'fill'
值caching
。所以这:
>>> proxy_img = nib.load(example_file)
>>> data = proxy_img.get_data() # default caching='fill'
>>> proxy_img.in_memory
True
是这样的:
>>> proxy_img = nib.load(example_file)
>>> data = proxy_img.get_data(caching='fill')
>>> proxy_img.in_memory
True
有时你可能想避免填充缓存,如果它是空的。在这种情况下,您可以使用caching='unchanged'
:
>>> proxy_img = nib.load(example_file)
>>> data = proxy_img.get_data(caching='unchanged')
>>> proxy_img.in_memory
False
caching='unchanged'
如果缓存已满,将会保持缓存满。
>>> data = proxy_img.get_data(caching='fill')
>>> proxy_img.in_memory
True
>>> data = proxy_img.get_data(caching='unchanged')
>>> proxy_img.in_memory
True
看到更多的细节。get_data()docstring
节省时间和内存
您可以使用阵列代理以有效的方式从磁盘获取片段。
数组代理API允许您在代理上进行切片。在大多数情况下,这意味着您只能从实际需要的磁盘加载数据,通常会节省时间和内存。
例如,让我们说你只想从示例数据集的第二卷。你可以这样做:
>>> proxy_img = nib.load(example_file)
>>> data = proxy_img.get_data()
>>> data.shape
(128, 96, 24, 2)
>>> vol1 = data[..., 1]
>>> vol1.shape
(128, 96, 24)
问题是,你必须将整个数据数组加载到内存中,然后丢弃第一个数据块并保留第二个数据块。
您可以使用阵列代理切片来更有效地执行此操作:
>>> proxy_img = nib.load(example_file)
>>> vol1 = proxy_img.dataobj[..., 1]
>>> vol1.shape
(128, 96, 24)
切入调用将只加载您需要填充内存的磁盘中的数据。proxy_img.dataobj[...,1]vol1